﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using Core;
using Core.Helper;
using Core.Model.Transmit;
using Core.Transmit;
using log4net;

namespace TcpServerChannel
{
    public sealed class TcpServerChannel : TransmitChannelProtocol
    {
        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private Thread _listenThread;
        private Thread _channelThread;
        private bool _running;
        private TcpListener _server { get; set; }
        private IPEndPoint _endPoint { get; }
        private int _writeTimeout, _readTimeout;
        private Dictionary<string,TcpClient> _tcpClients = new Dictionary<string, TcpClient>();
        private int _clientCount = 5;
        private object _synObj = new object();
       
        public TcpServerChannel(TransmitChannel channel)
        {
            try
            {
                ChannelNetType = NetType.TcpServer;
                ChlInfo = channel;
                ChlInfo.PollingDelay = channel.PollingDelay < 5 ? 5 : channel.PollingDelay;
                _readTimeout = ChlInfo.ReadTimeout < 500 ? ChlInfo.ReadTimeout : 500;
                _writeTimeout = ChlInfo.WriteTimeout < 500 ? ChlInfo.WriteTimeout : 500;
                string ip = ChlInfo.Ip;
                if (string.IsNullOrEmpty(ChlInfo.Ip))
                {
                    ip = NetHelper.GetLocalIp("127.0.0.1");
                }
                _endPoint = new IPEndPoint(IPAddress.Parse(ip), ChlInfo.Port);
                List<TransmitDevice> devices = Global.DataAccess.GetTransmitDevices(ChlInfo.Id);
                _server = new TcpListener(_endPoint);
                if (devices != null)
                {
                    foreach (TransmitDevice dev in devices)
                    {
                        LoadProtocol(dev);
                    }
                }
            }
            catch
            {
            }
        }

        private void ListenThreadProc()
        {
            while (_running)
            {
                try
                {
                    TcpClient client = _server.AcceptTcpClient();

                    lock (_synObj)
                    {
                        if (_tcpClients.Count < _clientCount)
                        {
                            if (client.Client.RemoteEndPoint is IPEndPoint ip)
                            {
                                _tcpClients.Add($"{ip.Address}:{ip.Port}",client);
                            }
                        }
                        else
                        {
                            if (client.Client.RemoteEndPoint is IPEndPoint ip)
                            {
                                _log.Warn($"因当前连接数已达{_clientCount},客户端{ip.Address}连接{ChlInfo.Name}被拒绝！");
                                client.Close();
                            }
                        
                        }
                    }
                }
                catch 
                {
                    
                }
                Thread.Sleep(1000);
            }

            try
            {
                _server.Stop();
            }
            catch 
            {   
            }
        }
        private void ThreadProc()
        {
            while (_running)
            {
                lock (_synObj)
                {
                    List<string> toRemove = new List<string>();
                    foreach (KeyValuePair<string, TcpClient> pair in _tcpClients)
                    {
                        try
                        {
                            TcpClient client = pair.Value;
                            if (client.GetState() != TcpState.Established)
                            {
                                toRemove.Add(pair.Key);
                            }
                            byte[] buffer = Read(client);
                            if(buffer==null || buffer.Length==0) continue;
                            foreach (KeyValuePair<string, TransmitDeviceProtocol> keyValuePair in protocols)
                            {
                                if (keyValuePair.Value.TryReplyCommand(buffer, out byte[] ackBuffer))
                                {
                                    if (ackBuffer != null && ackBuffer.Length > 0)
                                    {
                                        Write(client, ackBuffer);
                                        break;
                                    }
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                    }

                    foreach (string s in toRemove)
                    {
                        _tcpClients.Remove(s);
                    }
                }
                Thread.Sleep(ChlInfo.PollingDelay);
            }
        }
        private bool _connected = true;
        public override bool Connected
        {
            get
            {
                bool temp = false;
                if (_server != null)
                {
                    temp = _server.Pending();
                }
                if (!temp.Equals(_connected))
                {
                    _connected = temp;
                    OnPropertyChanged("Connected");
                }
                if (!_connected)
                {
                    foreach (KeyValuePair<string, TransmitDeviceProtocol> devProtocol in protocols)
                    {
                        devProtocol.Value.Dev.IsConnected = false;
                        devProtocol.Value.TransDataItems.ForEach(sss => sss.CurValue = 0);
                    }
                }
                return _connected;
            }
        }
        public override bool Start()
        {
            try
            {
                _running = false;
                Stop();
                _server = new TcpListener(_endPoint);
                _server.Start(10);
               
                _channelThread = new Thread(ThreadProc);
                _channelThread.IsBackground = true;
                _listenThread = new Thread(ListenThreadProc);
                _listenThread.IsBackground = true;
                _running = true;
                _listenThread.Start();
                _channelThread.Start();
                
            }
            catch (Exception ex)
            {
                lock (_synObj)
                {
                    _log.Error(ex.Message);
                }
              
                return false;
            }
            return true;
        }

        public override void Stop()
        {
            _running = false;
            try
            {
                _listenThread?.Abort();
                _server?.Stop();
                _server = null;
            }
            catch //(Exception e)
            {
                //lock (_synObj)
                //{
                //    _log.Error(e.Message);
                //}
            }
            try
            {
                _channelThread?.Abort();
                base.Stop();
            }
            catch //(Exception e)
            {
                //lock (_synObj)
                //{
                //    _log.Error(e.Message);
                //}
            }

            try
            {
                foreach (KeyValuePair<string, TcpClient> client in _tcpClients)
                {
                    client.Value.Close();
                }
            }
            catch 
            {
            }
        }

        private bool Write(TcpClient client,byte[] buffer)
        {
            try
            {
                if (buffer != null && buffer.Length > 0)
                {
                    client.SendTimeout = _writeTimeout;
                    NetworkStream stream = client.GetStream();
                    stream.Write(buffer, 0, buffer.Length);
                    stream.Flush();
                    return true;
                }
            }
            catch (Exception ex)
            {
                _log.Error(ex.Message);
            }
            return false;
        }

        private byte[] Read(TcpClient client)
        {

            try
            {
                client.ReceiveTimeout = _readTimeout;
                NetworkStream stream = client.GetStream();
                byte[] buffer = new byte[1500];
                int len = stream.Read(buffer, 0, buffer.Length);
                if (len > 0)
                {
                    if (len == buffer.Length)
                    {
                        return buffer;
                    }
                    else
                    {
                        byte[] recBytes = new byte[len];
                        Buffer.BlockCopy(buffer, 0, recBytes, 0, len);
                        return recBytes;
                    }
                }
            }
            catch
            {
                
            }

            return null;
        }

    }
}
